package com.example.doublecache.config;

import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.data.redis.cache.RedisCacheManager;

import java.util.concurrent.Callable;

/**
 * 双层缓存管理器
 * 实现了本地缓存(Caffeine)和分布式缓存(Redis)的双层缓存结构
 * 
 * 主要功能：
 * 1. 管理多个缓存实例
 * 2. 协调本地缓存和分布式缓存的访问
 * 3. 实现缓存数据的同步和一致性
 * 
 * 工作流程：
 * 1. 优先从本地缓存(Caffeine)获取数据
 * 2. 本地缓存未命中时，从Redis获取数据
 * 3. Redis命中时，将数据同步到本地缓存
 * 4. 写入操作同时更新两级缓存
 * 5. 删除操作同时清除两级缓存
 */
public class LayeredCacheManager extends SimpleCacheManager {
    // Caffeine本地缓存管理器，用于管理本地缓存实例
    private final CaffeineCacheManager caffeineCacheManager;
    // Redis分布式缓存管理器，用于管理Redis缓存实例
    private final RedisCacheManager redisCacheManager;
    // Redis操作模板，用于直接操作Redis（当前未使用）
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 构造函数
     * 初始化双层缓存管理器，注入本地缓存和分布式缓存管理器
     * 
     * @param caffeineCacheManager Caffeine本地缓存管理器，负责管理本地缓存实例
     * @param redisCacheManager Redis分布式缓存管理器，负责管理Redis缓存实例
     */
    public LayeredCacheManager(CaffeineCacheManager caffeineCacheManager, RedisCacheManager redisCacheManager) {
        this.caffeineCacheManager = caffeineCacheManager;
        this.redisCacheManager = redisCacheManager;
        this.redisTemplate = null;
    }

    /**
     * 获取指定名称的缓存实例
     * 如果缓存不存在，则创建一个新的双层缓存实例
     * 
     * @param name 缓存名称
     * @return 双层缓存实例
     */
    @Override
    protected Cache getMissingCache(String name) {
        return new LayeredCache(name, 
            caffeineCacheManager.getCache(name), 
            redisCacheManager.getCache(name));
    }

    /**
     * 双层缓存实现类
     * 实现了Cache接口，封装了本地缓存和分布式缓存的访问逻辑
     * 
     * 主要功能：
     * 1. 封装缓存的基本操作（get、put、evict、clear）
     * 2. 实现两级缓存的访问策略
     * 3. 保证两级缓存的数据一致性
     */
    private static class LayeredCache implements Cache {
        // 缓存名称
        private final String name;
        // Caffeine本地缓存实例
        private final Cache caffeineCache;
        // Redis分布式缓存实例
        private final Cache redisCache;

        /**
         * 构造函数
         * 初始化双层缓存实例
         * 
         * @param name 缓存名称
         * @param caffeineCache Caffeine本地缓存实例
         * @param redisCache Redis分布式缓存实例
         */
        public LayeredCache(String name, Cache caffeineCache, Cache redisCache) {
            this.name = name;
            this.caffeineCache = caffeineCache;
            this.redisCache = redisCache;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public Object getNativeCache() {
            return this;
        }

        /**
         * 获取缓存值
         * 优先从本地缓存获取，未命中时从Redis获取并回填本地缓存
         * 
         * 工作流程：
         * 1. 首先查询本地缓存
         * 2. 如果本地缓存命中，直接返回结果
         * 3. 如果本地缓存未命中，查询Redis缓存
         * 4. 如果Redis缓存命中，将数据同步到本地缓存后返回
         * 5. 如果Redis缓存也未命中，返回null
         * 
         * @param key 缓存键
         * @return 缓存值包装器，包含缓存值和元数据
         */
        @Override
        public ValueWrapper get(Object key) {
            // 先查本地缓存
            ValueWrapper value = caffeineCache.get(key);
            if (value != null) {
                return value;
            }

            // 本地缓存未命中，查Redis
            value = redisCache.get(key);
            if (value != null) {
                // 回填本地缓存，避免下次重复查询Redis
                caffeineCache.put(key, value.get());
            }
            return value;
        }

        /**
         * 获取指定类型的缓存值
         * 优先从本地缓存获取，未命中时从Redis获取并回填本地缓存
         * 
         * 工作流程：
         * 1. 首先查询本地缓存
         * 2. 如果本地缓存命中，直接返回结果
         * 3. 如果本地缓存未命中，查询Redis缓存
         * 4. 如果Redis缓存命中，将数据同步到本地缓存后返回
         * 5. 如果Redis缓存也未命中，返回null
         * 
         * @param key 缓存键
         * @param type 期望的返回值类型
         * @return 指定类型的缓存值
         */
        @Override
        public <T> T get(Object key, Class<T> type) {
            // 先查本地缓存
            T value = caffeineCache.get(key, type);
            if (value != null) {
                return value;
            }

            // 本地缓存未命中，查Redis
            value = redisCache.get(key, type);
            if (value != null) {
                // 回填本地缓存，避免下次重复查询Redis
                caffeineCache.put(key, value);
            }
            return value;
        }

        /**
         * 获取缓存值，如果不存在则通过valueLoader加载
         * 优先从本地缓存获取，未命中时从Redis获取并回填本地缓存
         * 
         * 工作流程：
         * 1. 首先查询本地缓存
         * 2. 如果本地缓存命中，直接返回结果
         * 3. 如果本地缓存未命中，查询Redis缓存
         * 4. 如果Redis缓存命中，将数据同步到本地缓存后返回
         * 5. 如果Redis缓存也未命中，使用valueLoader加载数据
         * 6. 加载的数据同时更新到本地缓存和Redis缓存
         * 
         * @param key 缓存键
         * @param valueLoader 值加载器，用于加载不存在的数据
         * @return 缓存值
         */
        @Override
        public <T> T get(Object key, Callable<T> valueLoader) {
            // 先查本地缓存
            T value = caffeineCache.get(key, valueLoader);
            if (value != null) {
                return value;
            }

            // 本地缓存未命中，查Redis
            value = redisCache.get(key, valueLoader);
            if (value != null) {
                // 回填本地缓存，避免下次重复查询Redis
                caffeineCache.put(key, value);
            }
            return value;
        }

        /**
         * 更新缓存值
         * 同时更新本地缓存和Redis缓存，保证数据一致性
         * 
         * 工作流程：
         * 1. 同时更新本地缓存和Redis缓存
         * 2. 保证两级缓存的数据一致性
         * 
         * @param key 缓存键
         * @param value 缓存值
         */
        @Override
        public void put(Object key, Object value) {
            // 同时更新两级缓存，保证数据一致性
            caffeineCache.put(key, value);
            redisCache.put(key, value);
        }

        /**
         * 删除缓存值
         * 同时删除本地缓存和Redis缓存，保证数据一致性
         * 
         * 工作流程：
         * 1. 同时删除本地缓存和Redis缓存
         * 2. 保证两级缓存的数据一致性
         * 
         * @param key 要删除的缓存键
         */
        @Override
        public void evict(Object key) {
            // 同时删除两级缓存，保证数据一致性
            caffeineCache.evict(key);
            redisCache.evict(key);
        }

        /**
         * 清空缓存
         * 同时清空本地缓存和Redis缓存，保证数据一致性
         * 
         * 工作流程：
         * 1. 同时清空本地缓存和Redis缓存
         * 2. 保证两级缓存的数据一致性
         */
        @Override
        public void clear() {
            // 同时清空两级缓存，保证数据一致性
            caffeineCache.clear();
            redisCache.clear();
        }
    }
} 